;gdt表项结构
;   _base[24,31]
;  |         _attr
;  |        |               _base[0,23]
;  |        |              |               _limit[0,15]   
;__|__ _____|_____ ________|________ _____|_____
;byte7 byte6 byte5 byte4 byte3 byte2 byte1 byte0
;      ===========
;          |
;          |
;          |
;          |     _G
;          |    |    _D/B
;          |    |   |    _O                   _P
;          |    |   |   |    _AVL           |    _DPL
;          |    |   |   |   |               |   |    _S
;          |    |   |   |   | limit[16,19]  |   |   |   type
;          |   _|_ _|_ _|_ _|_ ___|___     _|_ _|_ _|_ ___|___
;          |->  7   6   5   4  3 2 1 0      7  6 5  4  3 2 1 0 

;uasage Descriptor(dd base, dd limit, dw attr)  param start@%1
;limit:low 20 valid  attr:lower 4 bit of higher byte are allways 0
%macro Descriptor 3
dw %2 & 0xFFFF ;limit[0,15]
dw %1 & 0xFFFF ;base[0,15]
db (%1 >> 16) & 0xFF ;base[16, 23]
dw (%3 & 0xF0FF) | (%2 >> 8) & 0xF00 ;attr & limit[16,19]
db (%1 >> 24) & 0xFF ;base[24,31]
%endmacro

;P:存在位，1：在内存
;DPL表示描述符特权级（Descriptor Privilege Level），它规定了所描述段的特权级，以决定对该段能否访问。
;S：描述符的类型，对于存储段描述符而言，S=1，以区别与系统段描述符和门描述符（S=0）
;TYPE：说明存储段描述所描述的存储段的具体属性
;	数据段类型
;    0:只读  1:只读、已访问  2:读/写  3:读/写、已访问  4:只读、向下扩展  5:只读、向下扩展、已访问 
;    6:读/写、向下扩展  7:读/写、向下扩展、已访问 
;	代码段类型	----------------------------------
;    8:只执行  9:只执行、已访问   A:执行/读   B:执行/读、已访问   C:只执行、一致码段   
;    D:只执行、一致码段、已访问   E:执行/读、一致码段
;   系统段类型
;    0:<未定义>  1:可用286TSS  2:LDT  3:忙的286TSS  4:286调用门  5:任务门  6:286中断门  7:286陷阱门
;    8:未定义  9:可用386TSS  A:<未定义>  B:忙的386TSS  C:386调用门  D:<未定义>  E:386中断门  F:386陷阱门

;当 S=1 时TYPE中的4个二进制位情况：
;     3       2       1       0
;   执行位   一致位    读写位   访问位
;执行位：置1时表示可执行，置0时表示不可执行；
;一致位：置1时表示一致码段，置0时表示非一致码段；
;读写位：置1时表示可读可写，置0时表示只读；
;访问位：置1时表示已访问，置0时表示未访问。

;G:段界限粒度（Granularity） 0：1B   1：4k
; (6) D:    D位是一个很特殊的位，在描述可执行段、向下扩展数据段或由SS寄存器寻址的段(通常是堆栈段)的三种描述符中的意义各不相同。 
;           ⑴ 在描述可执行段的描述符中，D位决定了指令使用的地址及操作数所默认的大小。
;		① D=1表示默认情况下指令使用32位地址及32位或8位操作数，这样的代码段也称为32位代码段；
;		② D=0 表示默认情况下，使用16位地址及16位或8位操作数，这样的代码段也称为16位代码段，它与80286兼容。可以使用地址大小前缀和操作数大小前缀分别改变默认的地址或操作数的大小。 
;           ⑵ 在向下扩展数据段的描述符中，D位决定段的上部边界。
;		① D=1表示段的上部界限为4G；
;		② D=0表示段的上部界限为64K，这是为了与80286兼容。 
;           ⑶ 在描述由SS寄存器寻址的段描述符中，D位决定隐式的堆栈访问指令(如PUSH和POP指令)使用何种堆栈指针寄存器。
;		① D=1表示使用32位堆栈指针寄存器ESP；
;		② D=0表示使用16位堆栈指针寄存器SP，这与80286兼容。

;DA_:Descriptor Attribute    D:数据段    C:代码段    S:系统段    R:只读    RW   : 读写    A:已访问
; 描述符类型
DA_32		EQU	4000h	; 32 位段
DA_LIMIT_4K	EQU	8000h	; 段界限粒度为 4K 字节


DA_DPL0		EQU	  00h	; DPL = 0
DA_DPL1		EQU	  20h	; DPL = 1
DA_DPL2		EQU	  40h	; DPL = 2
DA_DPL3		EQU	  60h	; DPL = 3

; 存储段描述符类型
DA_DR		EQU	90h	; 存在的只读数据段类型值
DA_DRW		EQU	92h	; 存在的可读写数据段属性值
DA_DRWA		EQU	93h	; 存在的已访问可读写数据段类型值
DA_C		EQU	98h	; 存在的只执行代码段属性值
DA_CR		EQU	9Ah	; 存在的可执行可读代码段属性值
DA_CCO		EQU	9Ch	; 存在的只执行一致代码段属性值
DA_CCOR		EQU	9Eh	; 存在的可执行可读一致代码段属性值

; 系统段描述符类型
DA_LDT		EQU	  82h	; 局部描述符表段类型值
DA_TaskGate	EQU	  85h	; 任务门类型值
DA_386TSS	EQU	  89h	; 可用 386 任务状态段类型值
DA_386CGate	EQU	  8Ch	; 386 调用门类型值
DA_386IGate	EQU	  8Eh	; 386 中断门类型值
DA_386TGate	EQU	  8Fh	; 386 陷阱门类型值


;(gdt)门表项结构
;      _offset[31:16]
;     |            _attr
;     |           |            __selector
;     |           |           |            _offset[0,15]
;_____|_____ _____|_____ _____|_____ _____|_____
;byte7 byte6 byte5 byte4 byte3 byte2 byte1 byte0
;            ===========
;                 |        _P
;                 |       |    _DPL
;                 |       |   |     _S
;                 |       |   |    |       _TYPE
;                 |       |   |    |      |         fixed:0     _ParamCount
;                 |      _|_ _|_  _|_  ___|___       __|__ ____|____
;                 `-->    7  6 5   4   3 2 1 0       7 6 5 4 3 2 1 0 

;usage Gate(dw selector, dd offset, db DCount, db attr)
%macro Gate 4
dw (%2 & 0xFFFF) ;offset[0,15]
dw (%1 & 0xFFFF) ;selector
db (%3 & 0x1F) ;count
db %4 ;attr
dw ((%2 >> 16) & 0xFFFF)
%endmacro




;gdtr数据结构
;           |``gdt-base
;           |                 |``gdt-limit
;___________|___________ _____|_____
;byte5 byte4 byte3 byte2 byte1 byte0
 
;寄存器cr0数据结构
;31 30 29    ...   18 17 16 ...    05 04 03 02 01 00
;PG CD NW    resv  AM -  WP resv   NE ET TS EM MP PE
;PE  0:CPU实模式  1:保护模式
;PG  0:未分页     1:分页

;----------------------------------------------------------------------------
; 分页机制使用的常量说明
;----------------------------------------------------------------------------
PG_P        EQU 1   ; 页存在属性位
PG_RWR      EQU 0   ; R/W 属性位值, 读/执行
PG_RWW      EQU 2   ; R/W 属性位值, 读/写/执行
PG_USS      EQU 0   ; U/S 属性位值, 系统级
PG_USU      EQU 4   ; U/S 属性位值, 用户级
;----------------------------------------------------------------------------
;PDE 结构
;31 ...... 12  11...9  8   7  6  5   4   3   2   1   0
;PageTblBase   Avail   G  PS  0  A  PCD PWT U/S R/W  P
;P: Present  R/W: Read/Write  U/S: User/Supervisor  PWT:Write-through
;PCD: Cache disabled  A:Accessed  0:reserved(set to 0)  PS:Page Size(0 indicates 4K bytes)
;G:Global page(Ignored)  Avail:Available for system programer's use

;PTE 结构
;31 ...... 12  11...9  8   7   6  5   4   3   2   1   0
;PageTblBase   Avail   G  PAT  D  A  PCD PWT U/S R/W  P
;D: Dirty  PAT: Page Table Attribute Index 其它同PDE,详述如下
;・P存在位， 表示当前条目所指向的页或页表是否在物理内存中。 
;    P=0表示页不在内存中， 如果处理器试图访问此页， 将会产生页异常（ page-fault exception， #PF）；
;    P=1表示页在内存中。
;・R/ W 指定一个页或者一组页（ 比如， 此条目是指向页表的页目录条目） 的读写权限。 此位与U/S位和寄存器cr0中的WP位相互作用。
;    R/W=0表示只读； R/W=1表示可读并可写。
;・U/ S 指定一个页或者一组页（ 比如， 此条目是指向页表的页目录条目） 的特权级。 此位与R/W位和寄存器cr0中的WP位相互作用。 
;    U/S=0表示系统级别（ Supervisor Privilege Level） ， 如果CPL为0、 1或2， 那么它便是在此级别； 
;    U/S=1表示用户级别（ User Privilege Level）， 如果CPL为3， 那么它便是在此级别。
;  如果cr0中的WP位为0， 那么即便用户级（ User P.L.） 页面的R/W=0， 系统级（ Supervisor P.L.） 程序仍然具备写权限； 
;    如果WP位为1， 那么系统级（ Supervisor P.L.） 程序也不能写入用户级（ User P.L.） 只读页。
;  PW T 用于控制对单个页或者页表的缓冲策略。 PWT=0时使用Write-back缓冲策略； PWT=1时使用Write-through 缓冲策略。
;  当cr0寄存器的CD（ Cache-Disable） 位被设置时会被忽略。
;  PC D 用于控制对单个页或者页表的缓冲。 PCD=0时页或页表可以被缓冲； PCD=1时页或页表不可以被缓冲。
;  当cr0寄存器的CD（ Cache-Disable） 位被设置时会被忽略。
;・A指示页或页表是否被访问。 此位往往在页或页表刚刚被加载到物理内存中时被内存管理程序清零， 处理器会在第一次访问此页或页面时设置此位。 
;    而且， 处理器并不会自动清除此位， 只有软件能清除它。
;・D指示页或页表是否被写入。 此位往往在页或页表刚刚被加载到物理内存中时被内存管理程序清零， 处理器会在第一次写入此页或页面时设置此位。 
;    而且， 处理器并不会自动清除此位， 只有软件能清除它。
;・A位和D位都是被内存管理程序用来管理页和页表从物理内存中换入和换出的。
;・PS 决定页大小。 PS=0时页大小为4KB， PDE指向页表。
;・PAT 选择PAT（ Page Attribute Table） 条目。 Pentium III以后的CPU开始支持此位， 在此不予讨论， 并在我们的程序中设为0。
;・G指示全局页。 如果此位被设置， 同时cr4中的PGE位被置， 那么此页的页表或页目录条目不会在TLB中变得无效， 即便cr3被加载或者任务切换时也是如此。

;CPL DPL RPL
;CPL: Current Privilege Level 当前特权，是当前正在执行的代码所在的段的特权级，存在于cs寄存器的低两位
;DPL: Descriptor Privilege Level 描述符特权级,存储在段描述符中，规定访问该段的权限级别，每个段的DPL固定
;RPL: Request Privilege Level 请求特权级,保存在选择子的最低两位
;RPL说明的是进程对段访问的请求权限，意思是当前进程想要的请求权限。
;RPL的值由程序员自己来自由的设置，并不一定RPL>=CPL，但是当RPL<CPL时，实际起作用的就是CPL了，因为访问时的特权
;检查是判断：EPL=max(RPL,CPL)<=DPL是否成立，所以RPL可以看成是每次访问时的附加限制，RPL=0时附加限制最小，
;RPL=3时附加限制最大。所以你不要想通过来随便设置一个RPL来访问一个比CPL更内层的段。
;为甚麽在CPL之外增加一个RPL
;Intel手册上的解释为：The RPL can be used to insure that privileged code does not access a segment on behalf of an application program unless the program itself has access privileges for that segment.
;（RPL能够用来确保具有特权级的代码不会代表另一个应用程序去访问一个段，除非那个应用程序具有访问那个段的权限.）
;比方说：A进程的DPL为0，C进程的DPL为1，现在有一个B进程他的DPL为2，这B进程想委托A进程（外围程序可以访问一致代码段的内核）去访问C的数据（内核可以访问外围数据），
;如果没有RPL来限制的话，这样的委托访问是可以成功的，但这样是非常不安全的。
;有了RPL以后，A进程在访问C的时候还要受到RPL的约束，此时可以将访问C的选择子的RPL设为B的DPL，这样A的访问权限就相当为EPL=max（RPL，DPL）=2，这样他就无法代表B去越权访问C了。
;有RPL的情形，CPU同时检查CPL和RPL来判断是否允许对一个段的访问。在低特权级代码调用高特权级代码时，你可以把RPL认为是调用者CPL的影子。即使高特权级代码在运行，但它是应低特权级代码的请求，作为低特权级代码的代理在执行任务，在必要的时候，RPL作为对CPL的覆盖，可以削弱当前执行代码的可访问的区域，从而保证高特权级代码不会代表低特权级代码去访问一个后者没有访问权限的段。
;这里有一个问题，就是低特权级代码在向高特权级代码传递段选择子时，可以任意设置RPL。所以x86处理器有一条专门的指令ARPL用来纠正RPL

;选择子的数据结构
;           __offset              
;          |                 _TI 0:GDT 1:LDT
;          |                |    __RPL 
;__________|______________ _|_ _|_
;F E D C B A 9 8 7 6 5 4 3  2  1 0

;       SA_  : Selector Attribute
SA_RPL0     EQU 0
SA_RPL1     EQU 1
SA_RPL2     EQU 2
SA_RPL3     EQU 3

SA_TI_GDT      EQU 0
SA_TI_LDT      EQU 4